/**
 * we-cropper v1.3.9
 * (c) 2020 dlhandsome
 * @license MIT
 */
(function(global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
        typeof define === 'function' && define.amd ? define(factory) :
        (global.WeCropper = factory());
}(this, (function() {
    'use strict';

    var device = void 0;
    var TOUCH_STATE = ['touchstarted', 'touchmoved', 'touchended'];

    function firstLetterUpper(str) {
        return str.charAt(0).toUpperCase() + str.slice(1)
    }

    function setTouchState(instance) {
        var arg = [],
            len = arguments.length - 1;
        while (len-- > 0) arg[len] = arguments[len + 1];

        TOUCH_STATE.forEach(function(key, i) {
            if (arg[i] !== undefined) {
                instance[key] = arg[i];
            }
        });
    }

    function validator(instance, o) {
        Object.defineProperties(instance, o);
    }

    function getDevice() {
        if (!device) {
            device = uni.getSystemInfoSync();
        }
        return device
    }

    var tmp = {};

    var ref = getDevice();
    var pixelRatio = ref.pixelRatio;

    var DEFAULT = {
        id: {
            default: 'cropper',
            get: function get() {
                return tmp.id
            },
            set: function set(value) {
                if (typeof(value) !== 'string') {
                    console.error(("id：" + value + " is invalid"));
                }
                tmp.id = value;
            }
        },
        width: {
            default: 750,
            get: function get() {
                return tmp.width
            },
            set: function set(value) {
                if (typeof(value) !== 'number') {
                    console.error(("width：" + value + " is invalid"));
                }
                tmp.width = value;
            }
        },
        height: {
            default: 750,
            get: function get() {
                return tmp.height
            },
            set: function set(value) {
                if (typeof(value) !== 'number') {
                    console.error(("height：" + value + " is invalid"));
                }
                tmp.height = value;
            }
        },
        pixelRatio: {
            default: pixelRatio,
            get: function get() {
                return tmp.pixelRatio
            },
            set: function set(value) {
                if (typeof(value) !== 'number') {
                    console.error(("pixelRatio：" + value + " is invalid"));
                }
                tmp.pixelRatio = value;
            }
        },
        scale: {
            default: 2.5,
            get: function get() {
                return tmp.scale
            },
            set: function set(value) {
                if (typeof(value) !== 'number') {
                    console.error(("scale：" + value + " is invalid"));
                }
                tmp.scale = value;
            }
        },
        zoom: {
            default: 5,
            get: function get() {
                return tmp.zoom
            },
            set: function set(value) {
                if (typeof(value) !== 'number') {
                    console.error(("zoom：" + value + " is invalid"));
                } else if (value < 0 || value > 10) {
                    console.error("zoom should be ranged in 0 ~ 10");
                }
                tmp.zoom = value;
            }
        },
        src: {
            default: '',
            get: function get() {
                return tmp.src
            },
            set: function set(value) {
                if (typeof(value) !== 'string') {
                    console.error(("src：" + value + " is invalid"));
                }
                tmp.src = value;
            }
        },
        cut: {
            default: {},
            get: function get() {
                return tmp.cut
            },
            set: function set(value) {
                if (typeof(value) !== 'object') {
                    console.error(("cut：" + value + " is invalid"));
                }
                tmp.cut = value;
            }
        },
        boundStyle: {
            default: {},
            get: function get() {
                return tmp.boundStyle
            },
            set: function set(value) {
                if (typeof(value) !== 'object') {
                    console.error(("boundStyle：" + value + " is invalid"));
                }
                tmp.boundStyle = value;
            }
        },
        onReady: {
            default: null,
            get: function get() {
                return tmp.ready
            },
            set: function set(value) {
                tmp.ready = value;
            }
        },
        onBeforeImageLoad: {
            default: null,
            get: function get() {
                return tmp.beforeImageLoad
            },
            set: function set(value) {
                tmp.beforeImageLoad = value;
            }
        },
        onImageLoad: {
            default: null,
            get: function get() {
                return tmp.imageLoad
            },
            set: function set(value) {
                tmp.imageLoad = value;
            }
        },
        onBeforeDraw: {
            default: null,
            get: function get() {
                return tmp.beforeDraw
            },
            set: function set(value) {
                tmp.beforeDraw = value;
            }
        }
    };

    var ref$1 = getDevice();
    var windowWidth = ref$1.windowWidth;

    function prepare() {
        var self = this;

        // v1.4.0 版本中将不再自动绑定we-cropper实例
        self.attachPage = function() {
            var pages = getCurrentPages();
            // 获取到当前page上下文
            var pageContext = pages[pages.length - 1];
            // 把this依附在Page上下文的wecropper属性上，便于在page钩子函数中访问
            Object.defineProperty(pageContext, 'wecropper', {
                get: function get() {
                    console.warn(
                        'Instance will not be automatically bound to the page after v1.4.0\n\n' +
                        'Please use a custom instance name instead\n\n' +
                        'Example: \n' +
                        'this.mycropper = new WeCropper(options)\n\n' +
                        '// ...\n' +
                        'this.mycropper.getCropperImage()'
                    );
                    return self
                },
                configurable: true
            });
        };

        self.createCtx = function() {
            var id = self.id;
            var targetId = self.targetId;

            if (id) {
                self.ctx = self.ctx || uni.createCanvasContext(id);
                self.targetCtx = self.targetCtx || uni.createCanvasContext(targetId);
            } else {
                console.error("constructor: create canvas context failed, 'id' must be valuable");
            }
        };

        self.deviceRadio = windowWidth / 750;
    }

    var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !==
        'undefined' ? self : {};





    function createCommonjsModule(fn, module) {
        return module = {
            exports: {}
        }, fn(module, module.exports), module.exports;
    }

    var tools = createCommonjsModule(function(module, exports) {
        /**
         * String type check
         */
        exports.isStr = function(v) {
            return typeof v === 'string';
        };
        /**
         * Number type check
         */
        exports.isNum = function(v) {
            return typeof v === 'number';
        };
        /**
         * Array type check
         */
        exports.isArr = Array.isArray;
        /**
         * undefined type check
         */
        exports.isUndef = function(v) {
            return v === undefined;
        };

        exports.isTrue = function(v) {
            return v === true;
        };

        exports.isFalse = function(v) {
            return v === false;
        };
        /**
         * Function type check
         */
        exports.isFunc = function(v) {
            return typeof v === 'function';
        };
        /**
         * Quick object check - this is primarily used to tell
         * Objects from primitive values when we know the value
         * is a JSON-compliant type.
         */
        exports.isObj = exports.isObject = function(obj) {
            return obj !== null && typeof obj === 'object'
        };

        /**
         * Strict object type check. Only returns true
         * for plain JavaScript objects.
         */
        var _toString = Object.prototype.toString;
        exports.isPlainObject = function(obj) {
            return _toString.call(obj) === '[object Object]'
        };

        /**
         * Check whether the object has the property.
         */
        var hasOwnProperty = Object.prototype.hasOwnProperty;
        exports.hasOwn = function(obj, key) {
            return hasOwnProperty.call(obj, key)
        };

        /**
         * Perform no operation.
         * Stubbing args to make Flow happy without leaving useless transpiled code
         * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/)
         */
        exports.noop = function(a, b, c) {};

        /**
         * Check if val is a valid array index.
         */
        exports.isValidArrayIndex = function(val) {
            var n = parseFloat(String(val));
            return n >= 0 && Math.floor(n) === n && isFinite(val)
        };
    });

    var tools_7 = tools.isFunc;
    var tools_10 = tools.isPlainObject;

    var EVENT_TYPE = ['ready', 'beforeImageLoad', 'beforeDraw', 'imageLoad'];

    function observer() {
        var self = this;

        self.on = function(event, fn) {
            if (EVENT_TYPE.indexOf(event) > -1) {
                if (tools_7(fn)) {
                    event === 'ready' ?
                        fn(self) :
                        self[("on" + (firstLetterUpper(event)))] = fn;
                }
            } else {
                console.error(("event: " + event + " is invalid"));
            }
            return self
        };
    }

    function wxPromise(fn) {
        return function(obj) {
            var args = [],
                len = arguments.length - 1;
            while (len-- > 0) args[len] = arguments[len + 1];

            if (obj === void 0) obj = {};
            return new Promise(function(resolve, reject) {
                obj.success = function(res) {
                    resolve(res);
                };
                obj.fail = function(err) {
                    reject(err);
                };
                fn.apply(void 0, [obj].concat(args));
            })
        }
    }

    function draw(ctx, reserve) {
        if (reserve === void 0) reserve = false;

        return new Promise(function(resolve) {
            ctx.draw(reserve, resolve);
        })
    }

    var getImageInfo = wxPromise(uni.getImageInfo);

    var canvasToTempFilePath = wxPromise(uni.canvasToTempFilePath);

    var base64 = createCommonjsModule(function(module, exports) {
        /*! http://mths.be/base64 v0.1.0 by @mathias | MIT license */
        (function(root) {

            // Detect free variables `exports`.
            var freeExports = 'object' == 'object' && exports;

            // Detect free variable `module`.
            var freeModule = 'object' == 'object' && module &&
                module.exports == freeExports && module;

            // Detect free variable `global`, from Node.js or Browserified code, and use
            // it as `root`.
            var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal;
            if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
                root = freeGlobal;
            }

            /*--------------------------------------------------------------------------*/

            var InvalidCharacterError = function(message) {
                this.message = message;
            };
            InvalidCharacterError.prototype = new Error;
            InvalidCharacterError.prototype.name = 'InvalidCharacterError';

            var error = function(message) {
                // Note: the error messages used throughout this file match those used by
                // the native `atob`/`btoa` implementation in Chromium.
                throw new InvalidCharacterError(message);
            };

            var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
            // http://whatwg.org/html/common-microsyntaxes.html#space-character
            var REGEX_SPACE_CHARACTERS = /[\t\n\f\r ]/g;

            // `decode` is designed to be fully compatible with `atob` as described in the
            // HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob
            // The optimized base64-decoding algorithm used is based on @atk’s excellent
            // implementation. https://gist.github.com/atk/1020396
            var decode = function(input) {
                input = String(input)
                    .replace(REGEX_SPACE_CHARACTERS, '');
                var length = input.length;
                if (length % 4 == 0) {
                    input = input.replace(/==?$/, '');
                    length = input.length;
                }
                if (
                    length % 4 == 1 ||
                    // http://whatwg.org/C#alphanumeric-ascii-characters
                    /[^+a-zA-Z0-9/]/.test(input)
                ) {
                    error(
                        'Invalid character: the string to be decoded is not correctly encoded.'
                    );
                }
                var bitCounter = 0;
                var bitStorage;
                var buffer;
                var output = '';
                var position = -1;
                while (++position < length) {
                    buffer = TABLE.indexOf(input.charAt(position));
                    bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;
                    // Unless this is the first of a group of 4 characters…
                    if (bitCounter++ % 4) {
                        // …convert the first 8 bits to a single ASCII character.
                        output += String.fromCharCode(
                            0xFF & bitStorage >> (-2 * bitCounter & 6)
                        );
                    }
                }
                return output;
            };

            // `encode` is designed to be fully compatible with `btoa` as described in the
            // HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoa
            var encode = function(input) {
                input = String(input);
                if (/[^\0-\xFF]/.test(input)) {
                    // Note: no need to special-case astral symbols here, as surrogates are
                    // matched, and the input is supposed to only contain ASCII anyway.
                    error(
                        'The string to be encoded contains characters outside of the ' +
                        'Latin1 range.'
                    );
                }
                var padding = input.length % 3;
                var output = '';
                var position = -1;
                var a;
                var b;
                var c;
                var buffer;
                // Make sure any padding is handled outside of the loop.
                var length = input.length - padding;

                while (++position < length) {
                    // Read three bytes, i.e. 24 bits.
                    a = input.charCodeAt(position) << 16;
                    b = input.charCodeAt(++position) << 8;
                    c = input.charCodeAt(++position);
                    buffer = a + b + c;
                    // Turn the 24 bits into four chunks of 6 bits each, and append the
                    // matching character for each of them to the output.
                    output += (
                        TABLE.charAt(buffer >> 18 & 0x3F) +
                        TABLE.charAt(buffer >> 12 & 0x3F) +
                        TABLE.charAt(buffer >> 6 & 0x3F) +
                        TABLE.charAt(buffer & 0x3F)
                    );
                }

                if (padding == 2) {
                    a = input.charCodeAt(position) << 8;
                    b = input.charCodeAt(++position);
                    buffer = a + b;
                    output += (
                        TABLE.charAt(buffer >> 10) +
                        TABLE.charAt((buffer >> 4) & 0x3F) +
                        TABLE.charAt((buffer << 2) & 0x3F) +
                        '='
                    );
                } else if (padding == 1) {
                    buffer = input.charCodeAt(position);
                    output += (
                        TABLE.charAt(buffer >> 2) +
                        TABLE.charAt((buffer << 4) & 0x3F) +
                        '=='
                    );
                }

                return output;
            };

            var base64 = {
                'encode': encode,
                'decode': decode,
                'version': '0.1.0'
            };

            // Some AMD build optimizers, like r.js, check for specific condition patterns
            // like the following:
            if (
                typeof undefined == 'function' &&
                typeof undefined.amd == 'object' &&
                undefined.amd
            ) {
                undefined(function() {
                    return base64;
                });
            } else if (freeExports && !freeExports.nodeType) {
                if (freeModule) { // in Node.js or RingoJS v0.8.0+
                    freeModule.exports = base64;
                } else { // in Narwhal or RingoJS v0.7.0-
                    for (var key in base64) {
                        base64.hasOwnProperty(key) && (freeExports[key] = base64[key]);
                    }
                }
            } else { // in Rhino or a web browser
                root.base64 = base64;
            }

        }(commonjsGlobal));
    });

    function makeURI(strData, type) {
        return 'data:' + type + ';base64,' + strData
    }

    function fixType(type) {
        type = type.toLowerCase().replace(/jpg/i, 'jpeg');
        var r = type.match(/png|jpeg|bmp|gif/)[0];
        return 'image/' + r
    }

    function encodeData(data) {
        var str = '';
        if (typeof data === 'string') {
            str = data;
        } else {
            for (var i = 0; i < data.length; i++) {
                str += String.fromCharCode(data[i]);
            }
        }
        return base64.encode(str)
    }

    /**
     * 获取图像区域隐含的像素数据
     * @param canvasId canvas标识
     * @param x 将要被提取的图像数据矩形区域的左上角 x 坐标
     * @param y 将要被提取的图像数据矩形区域的左上角 y 坐标
     * @param width 将要被提取的图像数据矩形区域的宽度
     * @param height 将要被提取的图像数据矩形区域的高度
     * @param done 完成回调
     */
    function getImageData(canvasId, x, y, width, height, done) {
        uni.canvasGetImageData({
            canvasId: canvasId,
            x: x,
            y: y,
            width: width,
            height: height,
            success: function success(res) {
                done(res, null);
            },
            fail: function fail(res) {
                done(null, res);
            }
        });
    }

    /**
     * 生成bmp格式图片
     * 按照规则生成图片响应头和响应体
     * @param oData 用来描述 canvas 区域隐含的像素数据 { data, width, height } = oData
     * @returns {*} base64字符串
     */
    function genBitmapImage(oData) {
        //
        // BITMAPFILEHEADER: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx
        // BITMAPINFOHEADER: http://msdn.microsoft.com/en-us/library/dd183376.aspx
        //
        var biWidth = oData.width;
        var biHeight = oData.height;
        var biSizeImage = biWidth * biHeight * 3;
        var bfSize = biSizeImage + 54; // total header size = 54 bytes

        //
        //  typedef struct tagBITMAPFILEHEADER {
        //      WORD bfType;
        //      DWORD bfSize;
        //      WORD bfReserved1;
        //      WORD bfReserved2;
        //      DWORD bfOffBits;
        //  } BITMAPFILEHEADER;
        //
        var BITMAPFILEHEADER = [
            // WORD bfType -- The file type signature; must be "BM"
            0x42, 0x4D,
            // DWORD bfSize -- The size, in bytes, of the bitmap file
            bfSize & 0xff, bfSize >> 8 & 0xff, bfSize >> 16 & 0xff, bfSize >> 24 & 0xff,
            // WORD bfReserved1 -- Reserved; must be zero
            0, 0,
            // WORD bfReserved2 -- Reserved; must be zero
            0, 0,
            // DWORD bfOffBits -- The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits.
            54, 0, 0, 0
        ];

        //
        //  typedef struct tagBITMAPINFOHEADER {
        //      DWORD biSize;
        //      LONG  biWidth;
        //      LONG  biHeight;
        //      WORD  biPlanes;
        //      WORD  biBitCount;
        //      DWORD biCompression;
        //      DWORD biSizeImage;
        //      LONG  biXPelsPerMeter;
        //      LONG  biYPelsPerMeter;
        //      DWORD biClrUsed;
        //      DWORD biClrImportant;
        //  } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
        //
        var BITMAPINFOHEADER = [
            // DWORD biSize -- The number of bytes required by the structure
            40, 0, 0, 0,
            // LONG biWidth -- The width of the bitmap, in pixels
            biWidth & 0xff, biWidth >> 8 & 0xff, biWidth >> 16 & 0xff, biWidth >> 24 & 0xff,
            // LONG biHeight -- The height of the bitmap, in pixels
            biHeight & 0xff, biHeight >> 8 & 0xff, biHeight >> 16 & 0xff, biHeight >> 24 & 0xff,
            // WORD biPlanes -- The number of planes for the target device. This value must be set to 1
            1, 0,
            // WORD biBitCount -- The number of bits-per-pixel, 24 bits-per-pixel -- the bitmap
            // has a maximum of 2^24 colors (16777216, Truecolor)
            24, 0,
            // DWORD biCompression -- The type of compression, BI_RGB (code 0) -- uncompressed
            0, 0, 0, 0,
            // DWORD biSizeImage -- The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps
            biSizeImage & 0xff, biSizeImage >> 8 & 0xff, biSizeImage >> 16 & 0xff, biSizeImage >> 24 & 0xff,
            // LONG biXPelsPerMeter, unused
            0, 0, 0, 0,
            // LONG biYPelsPerMeter, unused
            0, 0, 0, 0,
            // DWORD biClrUsed, the number of color indexes of palette, unused
            0, 0, 0, 0,
            // DWORD biClrImportant, unused
            0, 0, 0, 0
        ];

        var iPadding = (4 - ((biWidth * 3) % 4)) % 4;

        var aImgData = oData.data;

        var strPixelData = '';
        var biWidth4 = biWidth << 2;
        var y = biHeight;
        var fromCharCode = String.fromCharCode;

        do {
            var iOffsetY = biWidth4 * (y - 1);
            var strPixelRow = '';
            for (var x = 0; x < biWidth; x++) {
                var iOffsetX = x << 2;
                strPixelRow += fromCharCode(aImgData[iOffsetY + iOffsetX + 2]) +
                    fromCharCode(aImgData[iOffsetY + iOffsetX + 1]) +
                    fromCharCode(aImgData[iOffsetY + iOffsetX]);
            }

            for (var c = 0; c < iPadding; c++) {
                strPixelRow += String.fromCharCode(0);
            }

            strPixelData += strPixelRow;
        } while (--y)

        var strEncoded = encodeData(BITMAPFILEHEADER.concat(BITMAPINFOHEADER)) + encodeData(strPixelData);

        return strEncoded
    }

    /**
     * 转换为图片base64
     * @param canvasId canvas标识
     * @param x 将要被提取的图像数据矩形区域的左上角 x 坐标
     * @param y 将要被提取的图像数据矩形区域的左上角 y 坐标
     * @param width 将要被提取的图像数据矩形区域的宽度
     * @param height 将要被提取的图像数据矩形区域的高度
     * @param type 转换图片类型
     * @param done 完成回调
     */
    function convertToImage(canvasId, x, y, width, height, type, done) {
        if (done === void 0) done = function() {};

        if (type === undefined) {
            type = 'png';
        }
        type = fixType(type);
        if (/bmp/.test(type)) {
            getImageData(canvasId, x, y, width, height, function(data, err) {
                var strData = genBitmapImage(data);
                tools_7(done) && done(makeURI(strData, 'image/' + type), err);
            });
        } else {
            console.error('暂不支持生成\'' + type + '\'类型的base64图片');
        }
    }

    var CanvasToBase64 = {
        convertToImage: convertToImage,
        // convertToPNG: function (width, height, done) {
        //   return convertToImage(width, height, 'png', done)
        // },
        // convertToJPEG: function (width, height, done) {
        //   return convertToImage(width, height, 'jpeg', done)
        // },
        // convertToGIF: function (width, height, done) {
        //   return convertToImage(width, height, 'gif', done)
        // },
        convertToBMP: function(ref, done) {
            if (ref === void 0) ref = {};
            var canvasId = ref.canvasId;
            var x = ref.x;
            var y = ref.y;
            var width = ref.width;
            var height = ref.height;
            if (done === void 0) done = function() {};

            return convertToImage(canvasId, x, y, width, height, 'bmp', done)
        }
    };

    function methods() {
        var self = this;

        var boundWidth = self.width; // 裁剪框默认宽度，即整个画布宽度
        var boundHeight = self.height; // 裁剪框默认高度，即整个画布高度

        var id = self.id;
        var targetId = self.targetId;
        var pixelRatio = self.pixelRatio;

        var ref = self.cut;
        var x = ref.x;
        if (x === void 0) x = 0;
        var y = ref.y;
        if (y === void 0) y = 0;
        var width = ref.width;
        if (width === void 0) width = boundWidth;
        var height = ref.height;
        if (height === void 0) height = boundHeight;

        self.updateCanvas = function(done) {
            if (self.croperTarget) {
                //  画布绘制图片
                self.ctx.drawImage(
                    self.croperTarget,
                    self.imgLeft,
                    self.imgTop,
                    self.scaleWidth,
                    self.scaleHeight
                );
            }
            tools_7(self.onBeforeDraw) && self.onBeforeDraw(self.ctx, self);

            self.setBoundStyle(self.boundStyle); //    设置边界样式

            self.ctx.draw(false, done);
            return self
        };

        self.pushOrigin = self.pushOrign = function(src) {
            self.src = src;

            tools_7(self.onBeforeImageLoad) && self.onBeforeImageLoad(self.ctx, self);

            return getImageInfo({
                    src: src
                })
                .then(function(res) {
                    var innerAspectRadio = res.width / res.height;
                    var customAspectRadio = width / height;

                    self.croperTarget = res.path;

                    if (innerAspectRadio < customAspectRadio) {
                        self.rectX = x;
                        self.baseWidth = width;
                        self.baseHeight = width / innerAspectRadio;
                        self.rectY = y - Math.abs((height - self.baseHeight) / 2);
                    } else {
                        self.rectY = y;
                        self.baseWidth = height * innerAspectRadio;
                        self.baseHeight = height;
                        self.rectX = x - Math.abs((width - self.baseWidth) / 2);
                    }

                    self.imgLeft = self.rectX;
                    self.imgTop = self.rectY;
                    self.scaleWidth = self.baseWidth;
                    self.scaleHeight = self.baseHeight;

                    self.update();

                    return new Promise(function(resolve) {
                        self.updateCanvas(resolve);
                    })
                })
                .then(function() {
                    tools_7(self.onImageLoad) && self.onImageLoad(self.ctx, self);
                })
        };

        self.removeImage = function() {
            self.src = '';
            self.croperTarget = '';
            return draw(self.ctx)
        };

        self.getCropperBase64 = function(done) {
            if (done === void 0) done = function() {};

            CanvasToBase64.convertToBMP({
                canvasId: id,
                x: x,
                y: y,
                width: width,
                height: height
            }, done);
        };

        self.getCropperImage = function(opt, fn) {
            var customOptions = opt;

            var canvasOptions = {
                canvasId: id,
                x: x,
                y: y,
                width: width,
                height: height
            };

            var task = function() {
                return Promise.resolve();
            };

            if (
                tools_10(customOptions) &&
                customOptions.original
            ) {
                // original mode
                task = function() {
                    self.targetCtx.drawImage(
                        self.croperTarget,
                        self.imgLeft * pixelRatio,
                        self.imgTop * pixelRatio,
                        self.scaleWidth * pixelRatio,
                        self.scaleHeight * pixelRatio
                    );

                    canvasOptions = {
                        canvasId: targetId,
                        x: x * pixelRatio,
                        y: y * pixelRatio,
                        width: width * pixelRatio,
                        height: height * pixelRatio
                    };

                    return draw(self.targetCtx)
                };
            }

            return task()
                .then(function() {
                    if (tools_10(customOptions)) {
                        canvasOptions = Object.assign({}, canvasOptions, customOptions);
                    }

                    if (tools_7(customOptions)) {
                        fn = customOptions;
                    }

                    var arg = canvasOptions.componentContext ?
                        [canvasOptions, canvasOptions.componentContext] :
                        [canvasOptions];

                    return canvasToTempFilePath.apply(null, arg)
                })
                .then(function(res) {
                    var tempFilePath = res.tempFilePath;

                    return tools_7(fn) ?
                        fn.call(self, tempFilePath, null) :
                        tempFilePath
                })
                .catch(function(err) {
                    if (tools_7(fn)) {
                        fn.call(self, null, err);
                    } else {
                        throw err
                    }
                })
        };
    }

    /**
     * 获取最新缩放值
     * @param oldScale 上一次触摸结束后的缩放值
     * @param oldDistance 上一次触摸结束后的双指距离
     * @param zoom 缩放系数
     * @param touch0 第一指touch对象
     * @param touch1 第二指touch对象
     * @returns {*}
     */
    var getNewScale = function(oldScale, oldDistance, zoom, touch0, touch1) {
        var xMove, yMove, newDistance;
        // 计算二指最新距离
        xMove = Math.round(touch1.x - touch0.x);
        yMove = Math.round(touch1.y - touch0.y);
        newDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));

        return oldScale + 0.001 * zoom * (newDistance - oldDistance)
    };

    function update() {
        var self = this;

        if (!self.src) {
            return
        }

        self.__oneTouchStart = function(touch) {
            self.touchX0 = Math.round(touch.x);
            self.touchY0 = Math.round(touch.y);
        };

        self.__oneTouchMove = function(touch) {
            var xMove, yMove;
            // 计算单指移动的距离
            if (self.touchended) {
                return self.updateCanvas()
            }
            xMove = Math.round(touch.x - self.touchX0);
            yMove = Math.round(touch.y - self.touchY0);

            var imgLeft = Math.round(self.rectX + xMove);
            var imgTop = Math.round(self.rectY + yMove);

            self.outsideBound(imgLeft, imgTop);

            self.updateCanvas();
        };

        self.__twoTouchStart = function(touch0, touch1) {
            var xMove, yMove, oldDistance;

            self.touchX1 = Math.round(self.rectX + self.scaleWidth / 2);
            self.touchY1 = Math.round(self.rectY + self.scaleHeight / 2);

            // 计算两指距离
            xMove = Math.round(touch1.x - touch0.x);
            yMove = Math.round(touch1.y - touch0.y);
            oldDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));

            self.oldDistance = oldDistance;
        };

        self.__twoTouchMove = function(touch0, touch1) {
            var oldScale = self.oldScale;
            var oldDistance = self.oldDistance;
            var scale = self.scale;
            var zoom = self.zoom;

            self.newScale = getNewScale(oldScale, oldDistance, zoom, touch0, touch1);

            //  设定缩放范围
            self.newScale <= 1 && (self.newScale = 1);
            self.newScale >= scale && (self.newScale = scale);

            self.scaleWidth = Math.round(self.newScale * self.baseWidth);
            self.scaleHeight = Math.round(self.newScale * self.baseHeight);
            var imgLeft = Math.round(self.touchX1 - self.scaleWidth / 2);
            var imgTop = Math.round(self.touchY1 - self.scaleHeight / 2);

            self.outsideBound(imgLeft, imgTop);

            self.updateCanvas();
        };

        self.__xtouchEnd = function() {
            self.oldScale = self.newScale;
            self.rectX = self.imgLeft;
            self.rectY = self.imgTop;
        };
    }

    var handle = {
        //  图片手势初始监测
        touchStart: function touchStart(e) {
            var self = this;
            var ref = e.touches;
            var touch0 = ref[0];
            var touch1 = ref[1];

            if (!self.src) {
                return
            }

            setTouchState(self, true, null, null);

            // 计算第一个触摸点的位置，并参照改点进行缩放
            self.__oneTouchStart(touch0);

            // 两指手势触发
            if (e.touches.length >= 2) {
                self.__twoTouchStart(touch0, touch1);
            }
        },

        //  图片手势动态缩放
        touchMove: function touchMove(e) {
            var self = this;
            var ref = e.touches;
            var touch0 = ref[0];
            var touch1 = ref[1];

            if (!self.src) {
                return
            }

            setTouchState(self, null, true);

            // 单指手势时触发
            if (e.touches.length === 1) {
                self.__oneTouchMove(touch0);
            }
            // 两指手势触发
            if (e.touches.length >= 2) {
                self.__twoTouchMove(touch0, touch1);
            }
        },

        touchEnd: function touchEnd(e) {
            var self = this;

            if (!self.src) {
                return
            }

            setTouchState(self, false, false, true);
            self.__xtouchEnd();
        }
    };

    function cut() {
        var self = this;
        var boundWidth = self.width; // 裁剪框默认宽度，即整个画布宽度
        var boundHeight = self.height;
        // 裁剪框默认高度，即整个画布高度
        var ref = self.cut;
        var x = ref.x;
        if (x === void 0) x = 0;
        var y = ref.y;
        if (y === void 0) y = 0;
        var width = ref.width;
        if (width === void 0) width = boundWidth;
        var height = ref.height;
        if (height === void 0) height = boundHeight;

        /**
         * 设置边界
         * @param imgLeft 图片左上角横坐标值
         * @param imgTop 图片左上角纵坐标值
         */
        self.outsideBound = function(imgLeft, imgTop) {
            self.imgLeft = imgLeft >= x ?
                x :
                self.scaleWidth + imgLeft - x <= width ?
                x + width - self.scaleWidth :
                imgLeft;

            self.imgTop = imgTop >= y ?
                y :
                self.scaleHeight + imgTop - y <= height ?
                y + height - self.scaleHeight :
                imgTop;
        };

        /**
         * 设置边界样式
         * @param color    边界颜色
         */
        self.setBoundStyle = function(ref) {
            if (ref === void 0) ref = {};
            var color = ref.color;
            if (color === void 0) color = '#04b00f';
            var mask = ref.mask;
            if (mask === void 0) mask = 'rgba(0, 0, 0, 0.3)';
            var lineWidth = ref.lineWidth;
            if (lineWidth === void 0) lineWidth = 1;

            var half = lineWidth / 2;
            var boundOption = [{
                    start: {
                        x: x - half,
                        y: y + 10 - half
                    },
                    step1: {
                        x: x - half,
                        y: y - half
                    },
                    step2: {
                        x: x + 10 - half,
                        y: y - half
                    }
                },
                {
                    start: {
                        x: x - half,
                        y: y + height - 10 + half
                    },
                    step1: {
                        x: x - half,
                        y: y + height + half
                    },
                    step2: {
                        x: x + 10 - half,
                        y: y + height + half
                    }
                },
                {
                    start: {
                        x: x + width - 10 + half,
                        y: y - half
                    },
                    step1: {
                        x: x + width + half,
                        y: y - half
                    },
                    step2: {
                        x: x + width + half,
                        y: y + 10 - half
                    }
                },
                {
                    start: {
                        x: x + width + half,
                        y: y + height - 10 + half
                    },
                    step1: {
                        x: x + width + half,
                        y: y + height + half
                    },
                    step2: {
                        x: x + width - 10 + half,
                        y: y + height + half
                    }
                }
            ];

            // 绘制半透明层
            self.ctx.beginPath();
            self.ctx.setFillStyle(mask);
            self.ctx.fillRect(0, 0, x, boundHeight);
            self.ctx.fillRect(x, 0, width, y);
            self.ctx.fillRect(x, y + height, width, boundHeight - y - height);
            self.ctx.fillRect(x + width, 0, boundWidth - x - width, boundHeight);
            self.ctx.fill();

            boundOption.forEach(function(op) {
                self.ctx.beginPath();
                self.ctx.setStrokeStyle(color);
                self.ctx.setLineWidth(lineWidth);
                self.ctx.moveTo(op.start.x, op.start.y);
                self.ctx.lineTo(op.step1.x, op.step1.y);
                self.ctx.lineTo(op.step2.x, op.step2.y);
                self.ctx.stroke();
            });
        };
    }

    var version = "1.3.9";

    var WeCropper = function WeCropper(params) {
        var self = this;
        var _default = {};

        validator(self, DEFAULT);

        Object.keys(DEFAULT).forEach(function(key) {
            _default[key] = DEFAULT[key].default;
        });
        Object.assign(self, _default, params);

        self.prepare();
        self.attachPage();
        self.createCtx();
        self.observer();
        self.cutt();
        self.methods();
        self.init();
        self.update();

        return self
    };

    WeCropper.prototype.init = function init() {
        var self = this;
        var src = self.src;

        self.version = version;

        typeof self.onReady === 'function' && self.onReady(self.ctx, self);

        if (src) {
            self.pushOrign(src);
        } else {
            self.updateCanvas();
        }
        setTouchState(self, false, false, false);

        self.oldScale = 1;
        self.newScale = 1;

        return self
    };

    Object.assign(WeCropper.prototype, handle);

    WeCropper.prototype.prepare = prepare;
    WeCropper.prototype.observer = observer;
    WeCropper.prototype.methods = methods;
    WeCropper.prototype.cutt = cut;
    WeCropper.prototype.update = update;

    return WeCropper;

})));
